home *** CD-ROM | disk | FTP | other *** search
/ Delphi 5 for Professionals / DELPHI5.iso / AddOns / Components / Orpheus v3.02 / SETUP.EXE / %MAINDIR% / OvcStr.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-02-25  |  55.2 KB  |  1,836 lines

  1. {*********************************************************}
  2. {*                   OVCSTR.PAS 3.00                     *}
  3. {*     Copyright (c) 1995-99 TurboPower Software Co      *}
  4. {*                 All rights reserved.                  *}
  5. {*********************************************************}
  6.  
  7. {$I OVC.INC}
  8.  
  9. {$B-} {Complete Boolean Evaluation}
  10. {$I+} {Input/Output-Checking}
  11. {$P+} {Open Parameters}
  12. {$T-} {Typed @ Operator}
  13. {$W-} {Windows Stack Frame}
  14. {$X+} {Extended Syntax}
  15.  
  16. {$IFNDEF Win32}
  17. {$G+} {286 Instructions}
  18. {$N+} {Numeric Coprocessor}
  19.  
  20. {$C MOVEABLE,DEMANDLOAD,DISCARDABLE}
  21. {$ENDIF}
  22.  
  23. unit OvcStr;
  24.   {-General string handling routines}
  25.  
  26. interface
  27.  
  28. uses
  29.   OvcData;
  30.  
  31. type
  32.   BTable = array[0..255] of Byte;
  33.   {table used by the Boyer-Moore search routines}
  34.  
  35. function BinaryBPChar(Dest : PAnsiChar; B : Byte) : PAnsiChar;
  36.   {-Return a binary PAnsiChar string for a byte}
  37. function BinaryLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  38.   {-Return the binary PAnsiChar string for a long integer}
  39. function BinaryWPChar(Dest : PAnsiChar; W : Word) : PAnsiChar;
  40.   {-Return the binary PAnsiChar string for a word}
  41. procedure BMMakeTable(MatchString : PAnsiChar; var BT : BTable);
  42.   {-Build a Boyer-Moore link table}
  43. function BMSearch(var Buffer; BufLength : Cardinal; var BT : BTable;
  44.                   MatchString : PAnsiChar ; var Pos : Cardinal) : Boolean;
  45.   {-Use the Boyer-Moore search method to search a buffer for a string}
  46. function BMSearchUC(var Buffer; BufLength : Cardinal; var BT : BTable;
  47.                     MatchString : PAnsiChar ; var Pos : Cardinal) : Boolean;
  48.   {-Use the Boyer-Moore search method to search a buffer for a string. This
  49.     search is not case sensitive}
  50. function CharStrPChar(Dest : PAnsiChar; C : AnsiChar; Len : Cardinal) : PAnsiChar;
  51.   {-Return a PAnsiChar string filled with the specified character}
  52. function DetabPChar(Dest : PAnsiChar; Src : PAnsiChar; TabSize : Byte) : PAnsiChar;
  53.   {-Expand tabs in a PAnsiChar string to blanks}
  54. function HexBPChar(Dest : PAnsiChar; B : Byte) : PAnsiChar;
  55.   {-Return hex PAnsiChar string for byte}
  56. function HexLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  57.   {-Return the hex PAnsiChar string for a long integer}
  58. function HexPtrPChar(Dest : PAnsiChar; P : Pointer) : PAnsiChar;
  59.   {-Return hex PAnsiChar string for pointer}
  60. function HexWPChar(Dest : PAnsiChar; W : Word) : PAnsiChar;
  61.   {-Return the hex PAnsiChar string for a word}
  62. function LoCaseChar(C : AnsiChar) : AnsiChar;
  63.   {-Convert C to lower case}
  64. function OctalLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  65.   {-Return the octal PAnsiChar string for a long integer}
  66. function StrChDeletePrim(P : PAnsiChar; Pos : Cardinal) : PAnsiChar;
  67.   {-Primitive routine to delete a character from a PAnsiChar string}
  68. function StrChInsertPrim(Dest : PAnsiChar; C : AnsiChar; Pos : Cardinal) : PAnsiChar;
  69.   {-Primitive routine to insert a character into a PAnsiChar string}
  70. function StrChPos(P : PAnsiChar; C : AnsiChar; var Pos : Cardinal) : Boolean;
  71.   {-Sets Pos to location of C in P, return is True if found}
  72. procedure StrInsertChars(Dest : PAnsiChar; Ch : AnsiChar; Pos, Count : Word);
  73.   {-Insert count instances of Ch into S at Pos}
  74. function StrStCopy(Dest, S : PAnsiChar; Pos, Count : Cardinal) : PAnsiChar;
  75.   {-Copy characters at a specified position in a PAnsiChar string}
  76. function StrStDeletePrim(P : PAnsiChar; Pos, Count : Cardinal) : PAnsiChar;
  77.   {-Primitive routine to delete a sub-string from a PAnsiChar string}
  78. function StrStInsert(Dest, S1, S2 : PAnsiChar; Pos : Cardinal) : PAnsiChar;
  79.   {-Insert a PAnsiChar string into another at a specified position}
  80. function StrStInsertPrim(Dest, S : PAnsiChar; Pos : Cardinal) : PAnsiChar;
  81.   {-Insert a PAnsiChar string into another at a specified position. This
  82.     primitive version modifies the source directly}
  83. function StrStPos(P, S : PAnsiChar; var Pos : Cardinal) : Boolean;
  84.   {-Sets Pos to position of the S in P, returns True if found}
  85. function StrToLongPChar(S : PAnsiChar; var I : LongInt) : Boolean;
  86.   {-Convert a PAnsiChar string to a long integer}
  87. {$IFNDEF Win32}
  88. function StringOfChar(C : Char; Len : Cardinal) : string;
  89.   {-return a string filled with the specified character.}
  90. function Trim(const S : string) : string;
  91.   {-return a string with leading and trailing white space removed}
  92. {$ENDIF}
  93. procedure TrimAllSpacesPChar(P : PAnsiChar);
  94.   {-Trim leading and trailing blanks from P}
  95. function TrimEmbeddedZeros(const S : string) : string;
  96.   {-Trim embedded zeros from a numeric string in exponential format}
  97. procedure TrimEmbeddedZerosPChar(P : PAnsiChar);
  98.   {-Trim embedded zeros from a numeric PAnsiChar string in exponential format}
  99. function TrimTrailPrimPChar(S : PAnsiChar) : PAnsiChar;
  100.   {-Return a PAnsiChar string with trailing white space removed}
  101. function TrimTrailPChar(Dest, S : PAnsiChar) : PAnsiChar;
  102.   {-Return a PAnsiChar string with trailing white space removed}
  103. function TrimTrailingZeros(const S : string) : string;
  104.   {-Trim trailing zeros from a numeric string. It is assumed that there is
  105.     a decimal point prior to the zeros. Also strips leading spaces.}
  106. procedure TrimTrailingZerosPChar(P : PAnsiChar);
  107.   {-Trim trailing zeros from a numeric PAnsiChar string. It is assumed that
  108.     there is a decimal point prior to the zeros. Also strips leading spaces.}
  109. function UpCaseChar(C : AnsiChar) : AnsiChar;
  110.   {-Convert a character to uppercase using the AnsiUpper API}
  111.  
  112.  
  113. implementation
  114.  
  115. uses
  116.   {$IFDEF Win32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF}
  117.   SysUtils;
  118.  
  119. const
  120.   Digits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
  121.  
  122.  
  123. function BinaryBPChar(Dest : PAnsiChar; B : Byte) : PAnsiChar;
  124.   {-Return binary string for byte}
  125. var
  126.   I : Word;
  127. begin
  128.   Result := Dest;
  129.   for I := 7 downto 0 do begin
  130.     Dest^ := Digits[Ord(B and (1 shl I) <> 0)]; {0 or 1}
  131.     Inc(Dest);
  132.   end;
  133.   Dest^ := #0;
  134. end;
  135.  
  136. function BinaryLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  137.   {-Return binary string for LongInt}
  138. var
  139.   I : LongInt;
  140. begin
  141.   Result := Dest;
  142.   for I := 31 downto 0 do begin
  143.     Dest^ := Digits[Ord(L and LongInt(1 shl I) <> 0)]; {0 or 1}
  144.     Inc(Dest);
  145.   end;
  146.   Dest^ := #0;
  147. end;
  148.  
  149. function BinaryWPChar(Dest : PAnsiChar; W : Word) : PAnsiChar;
  150.   {-Return binary string for word}
  151. var
  152.   I : Word;
  153. begin
  154.   Result := Dest;
  155.   for I := 15 downto 0 do begin
  156.     Dest^ := Digits[Ord(W and (1 shl I) <> 0)]; {0 or 1}
  157.     Inc(Dest);
  158.   end;
  159.   Dest^ := #0;
  160. end;
  161.  
  162. {$IFDEF WIN32}
  163. procedure BMMakeTable(MatchString : PAnsiChar; var BT : BTable); register;
  164.   {Build Boyer-Moore link table}
  165. asm
  166.   push  esi             { Save registers because they will be changed }
  167.   push  edi
  168.   push  ebx
  169.  
  170.   cld                   { Ensure forward string ops }
  171.   mov   edi, eax        { Move EAX to ESI & EDI }
  172.   mov   esi, eax
  173.   xor   eax, eax        { Zero EAX }
  174.   or    ecx, -1
  175.   repne scasb           { Search for null terminator }
  176.   not   ecx
  177.   dec   ecx             { ECX is length of search string }
  178.   cmp   ecx, 0FFh       { If ECX > 255, force to 255 }
  179.   jbe   @@1
  180.   mov   ecx, 0FFh
  181.  
  182. @@1:
  183.   mov   ch, cl          { Duplicate CL in CH }
  184.   mov   eax, ecx        { Fill each byte in EAX with length }
  185.   shl   eax, 16
  186.   mov   ax, cx
  187.   mov   edi, edx        { Point to the table }
  188.   mov   ecx, 64         { Fill table bytes with length }
  189.   rep   stosd
  190.   cmp   al, 1           { If length >= 1, we're done }
  191.   jbe   @@MTDone
  192.   mov   edi, edx        { Reset EDI to beginning of table }
  193.   xor   ebx, ebx        { Zero EBX }
  194.   mov   cl, al          { Restore CL to length of string }
  195.   dec   ecx
  196.  
  197. @@MTNext:
  198.   lodsb                 { Load table with positions of letters }
  199.   mov   bl, al          { That exist in the search string }
  200.   mov   [edi+ebx], cl
  201.   loop  @@MTNext
  202.  
  203. @@MTDone:
  204.   pop   ebx             { Restore registers }
  205.   pop   edi
  206.   pop   esi
  207. end;
  208. {$ELSE}
  209. procedure BMMakeTable(MatchString : PAnsiChar; var BT : BTable); assembler;
  210.   {Build Boyer-Moore link table}
  211. asm
  212.   MOV    DX,DS                    {Save DS in DX}
  213.   CLD                             {Go forward}
  214.   LES    DI,MatchString
  215.   XOR    AL,AL
  216.   MOV    CX,0FFFFh
  217.   REPNE  SCASB
  218.   NOT    CX
  219.   DEC    CX
  220.   MOV    AX,CX
  221.   LDS    SI,MatchString           {DS:SI => MatchString}
  222.   LES    DI,BT                    {ES:DI => BT}
  223.   MOV    BX,DI                    {Save DI in BX}
  224.   MOV    AH,AL                    {Copy it to AH}
  225.   MOV    CX,128                   {Number of words in BT}
  226.   REP    STOSW                    {Fill BT with length(MatchString)}
  227.   CMP    AL,1                     {Is length(MatchString) <= 1?}
  228.   JBE    @@MTDONE                 {Yes, we're done}
  229.  
  230.   MOV    DI,BX                    {Restore base of table from BX}
  231.   MOV    BH,CH                    {BH = 0}
  232.   MOV    CL,AL                    {CX = length(MatchString)}
  233.   DEC    CX                       {CX = length(MatchString)-1}
  234.  
  235. @@MTnext:
  236.   LODSB                           {AL = MatchString[i]}
  237.   MOV    BL,AL                    {BL = MatchString[i]}
  238.   MOV    ES:[BX+DI],CL            {BTable[char] = length(MatchString)-i}
  239.   LOOP   @@MTnext                 {Repeat for all characters in MatchString}
  240.  
  241. @@MTDone:
  242.   MOV    DS,DX                    {Restore DS from DX}
  243. end;
  244. {$ENDIF}
  245.  
  246. {$IFDEF Win32}
  247. function BMSearch(var Buffer; BufLength : Cardinal; var BT : BTable;
  248.   MatchString : PAnsiChar; var Pos : Cardinal) : Boolean; register;
  249. var
  250.   BufPtr : Pointer;
  251. asm
  252.   push  edi                 { Save registers since we will be changing }
  253.   push  esi
  254.   push  ebx
  255.   push  edx
  256.  
  257.   mov   BufPtr, eax         { Copy Buffer to local variable and ESI }
  258.   mov   esi, eax
  259.   mov   ebx, ecx            { Copy BufLength to EBX }
  260.  
  261.   cld                       { Ensure forward string ops }
  262.   xor   eax, eax            { Zero out EAX so we can search for null }
  263.   mov   edi, MatchString    { Set EDI to beginning of MatchString }
  264.   or    ecx, -1             { We will be counting down }
  265.   repne scasb               { Find null }
  266.   not   ecx                 { ECX = length of MatchString + null }
  267.   dec   ecx                 { ECX = length of MatchString }
  268.   mov   edx, ecx            { Copy length of MatchString to EDX }
  269.  
  270.   pop   ecx                 { Pop length of buffer into ECX }
  271.   mov   edi, esi            { Set EDI to beginning of search buffer }
  272.   mov   esi, MatchString    { Set ESI to beginning of MatchString }
  273.  
  274.   cmp   dl, 1               { Check to see if we have a trivial case }
  275.   ja    @@BMSInit           { If Length(MatchString) > 1 do BM search }
  276.   jb    @@BMSNotFound       { If Length(MatchString) = 0 we're done }
  277.  
  278.   mov   al,[esi]            { If Length(MatchString) = 1 do a REPNE SCASB }
  279.   mov   ebx, edi
  280.   repne scasb
  281.   jne   @@BMSNotFound       { No match during REP SCASB }
  282.   dec   edi                 { Found, calculate position }
  283.   sub   edi, ebx
  284.   mov   esi, Pos            { Set position in Pos }
  285.   mov   [esi], edi
  286.   mov   eax, 1              { Set result to True }
  287.   jmp   @@BMSDone           { We're done }
  288.  
  289. @@BMSInit:
  290.   dec   edx                 { Set up for BM Search }
  291.   add   esi, edx            { Set ESI to end of MatchString }
  292.   add   ecx, edi            { Set ECX to end of buffer }
  293.   add   edi, edx            { Set EDI to first check point }
  294.   mov   dh, [esi]           { Set DH to character we'll be looking for }
  295.   dec   esi                 { Dec ESI in prep for BMSFound loop }
  296.   std                       { Backward string ops }
  297.   jmp   @@BMSComp           { Jump to first comparison }
  298.  
  299. @@BMSNext:
  300.   mov   al, [ebx+eax]       { Look up skip distance from table }
  301.   add   edi, eax            { Skip EDI ahead to next check point }
  302.  
  303. @@BMSComp:
  304.   cmp   edi, ecx            { Have we reached end of buffer? }
  305.   jae   @@BMSNotFound       { If so, we're done }
  306.   mov   al, [edi]           { Move character from buffer into AL for comparison }
  307.   cmp   dh, al              { Compare }
  308.   jne   @@BMSNext           { If not equal, go to next checkpoint }
  309.  
  310.   push  ecx                 { Save ECX }
  311.   dec   edi
  312.   xor   ecx, ecx            { Zero ECX }
  313.   mov   cl, dl              { Move Length(MatchString) to ECX }
  314.   repe  cmpsb               { Compare MatchString to buffer }
  315.   je    @@BMSFound          { If equal, string is found }
  316.  
  317.   mov   al, dl              { Move Length(MatchString) to AL }
  318.   sub   al, cl              { Calculate offset that string didn't match }
  319.   add   esi, eax            { Move ESI back to end of MatchString }
  320.   add   edi, eax            { Move EDI to pre-string compare location }
  321.   inc   edi
  322.   mov   al, dh              { Move character back to AL }
  323.   pop   ecx                 { Restore ECX }
  324.   jmp   @@BMSNext           { Do another compare }
  325.  
  326. @@BMSFound:                 { EDI points to start of match }
  327.   mov   edx, BufPtr         { Move pointer to buffer into EDX }
  328.   sub   edi, edx            { Calculate position of match }
  329.   mov   eax, edi
  330.   inc   eax
  331.   mov   esi, Pos
  332.   mov   [esi], eax          { Set Pos to position of match }
  333.   mov   eax, 1              { Set result to True }
  334.   pop   ecx                 { Restore ESP }
  335.   jmp   @@BMSDone
  336.  
  337. @@BMSNotFound:
  338.   xor   eax, eax            { Set result to False }
  339.  
  340. @@BMSDone:
  341.   cld                       { Restore direction flag }
  342.   pop   ebx                 { Restore registers }
  343.   pop   esi
  344.   pop   edi
  345. end;
  346. {$ELSE}
  347. function BMSearch(var Buffer; BufLength : Cardinal; var BT : BTable;
  348.   MatchString : PAnsiChar; var Pos : Cardinal) : Boolean; assembler;
  349. asm
  350.   PUSH   DS                       {Will wipe out DS}
  351.   PUSH   BP                       {Will use BP for temp storage later}
  352.   XOR    AX,AX
  353.   XOR    DX,DX
  354.   CLD
  355.   LES    DI,MatchString
  356.   MOV    CX,0FFFFh
  357.   REPNE  SCASB
  358.   NOT    CX
  359.   DEC    CX
  360.   MOV    DL,CL
  361.  
  362.   MOV    CX,BufLength             {CX = Buffer size}
  363.   LES    DI,Buffer                {ES:DI => Buffer}
  364.   LDS    BX,BT                    {DS:BX => BTable}
  365.   MOV    AX,DS                    {Keep BTable segment in AX a moment}
  366.   LDS    SI,MatchString           {DS:SI => MatchString}
  367.   MOV    BP,AX                    {Keep BTable segment in BP}
  368.  
  369.   XOR    AX,AX                    {AX = 0}
  370.   CMP    DL,1                     {Check for trivial cases}
  371.   JA     @@BMSinit                {Do Boyer-Moore if longer than one char}
  372.   JB     @@BMSnotFound            {Fail for empty string}
  373.  
  374.   MOV    AL,[SI]                  {AL = one and only char to find}
  375.   MOV    BX,DI                    {Save offset of Buffer}
  376.   CLD                             {Forward}
  377.   REPNE  SCASB                    {Scan Buffer for AL}
  378.   JNE    @@BMSnotFound            {Char wasn't found}
  379.   MOV    AX,DI                    {AX holds offset where char was found}
  380.   DEC    AX                       {Back up one}
  381.   SUB    AX,BX                    {Subtract base offset of Buffer}
  382.  
  383.   POP    BP                       {Get frame pointer back}
  384.   LES    DI,Pos                   {Get address of Pos}
  385.   MOV    ES:[DI],AX               {Store position offset in Pos}
  386.   MOV    AX,1                     {Set result}
  387.   JMP    @@BMSdone2               {We're done}
  388.   {JMP    @@BMSdone}              {We're done}
  389.  
  390. @@BMSinit:
  391.   DEC    DL                       {DX = length(MatchString)-1}
  392.   ADD    SI,DX                    {DS:SI => MatchString[length(MatchString)-1]}
  393.   ADD    CX,DI                    {CX = offset of last char in buffer}
  394.   ADD    DI,DX                    {ES:DI => first position to search}
  395.   MOV    DH,[SI]
  396.   DEC    SI
  397.   STD                             {Go backwards}
  398.   JMP    @@BMScomp                {Skip link table first time}
  399.  
  400. @@BMSnext:
  401.   PUSH   DS                       {Save DS a moment}
  402.   MOV    DS,BP                    {Get segment of link table}
  403.   XLAT                            {Get size of link at DS:[BX+AL]}
  404.   POP    DS                       {Restore DS}
  405.   ADD    DI,AX                    {Compute next place to search}
  406.  
  407. @@BMScomp:
  408.   JC     @@BMSnotFound            {Done if overflowed 64K}
  409.   CMP    DI,CX                    {At end of buffer?}
  410.   JAE    @@BMSnotFound            {Done if so}
  411.   MOV    AL,ES:[DI]               {AL = next char to try}
  412.   CMP    DH,AL                    {Does it match the end of MatchString?}
  413.   JNE    @@BMSnext                {If not same, go back and try again}
  414.  
  415.   PUSH   CX                       {Save end of buffer position}
  416.   DEC    DI                       {Start comparing one character before}
  417.   MOV    CL,DL                    {Compare length(MatchString)-1 characters}
  418.   MOV    CH,AH                    {CH = 0}
  419.   REPE   CMPSB                    {Compare backwards while matched}
  420.   JE     @@BMSfound               {Matched!}
  421.  
  422.   MOV    AL,DL                    {Restore SI,DI,AL}
  423.   SUB    AL,CL
  424.   ADD    SI,AX
  425.   ADD    DI,AX
  426.   INC    DI
  427.   MOV    AL,DH                    {Put matched char back in AL}
  428.   POP    CX                       {Restore end of buffer}
  429.   JMP    @@BMSnext                {Try again}
  430.  
  431. @@BMSfound:                       {DI points to start of match}
  432.   INC    SP                       {End of buffer off stack}
  433.   INC    SP
  434.   POP    BP                       {Get frame pointer back}
  435.   SUB    DI,WORD PTR Buffer       {Subtract buffer start address}
  436.   MOV    AX,DI
  437.   INC    AX                       {Return 0 if found in first byte}
  438.   les    di,Pos
  439.   mov    es:[di],ax               {Set Pos}
  440.   mov    ax,1                     {Result = True}
  441.   JMP    @@BMSDone2               {We're done}
  442.  
  443. @@BMSnotFound:
  444.   {MOV    AX,0FFFFh}
  445.   xor    ax, ax                   {Result = False}
  446. @@BMSDone:
  447.   POP    BP
  448. @@BMSDone2:
  449.   CLD
  450.   POP    DS
  451. end;
  452. {$ENDIF}
  453.  
  454. {$IFNDEF Win32}
  455. procedure UpcasePrim; near; assembler;
  456.   {-call the Windows AnsiUpper function}
  457. asm
  458.   push    bx
  459.   push    cx
  460.   push    dx
  461.   push    si
  462.   push    di
  463.   push    es
  464.   push    ax
  465.   xor     ah,ah
  466.   xor     bx,bx
  467.   push    bx
  468.   push    ax
  469.   call    AnsiUpper
  470.   pop     bx
  471.   mov     ah,bh
  472.   pop     es
  473.   pop     di
  474.   pop     si
  475.   pop     dx
  476.   pop     cx
  477.   pop     bx
  478. end;
  479. {$ENDIF}
  480.  
  481.  
  482. {$IFDEF Win32}
  483. function BMSearchUC(var Buffer; BufLength : Cardinal; var BT : BTable;
  484.   MatchString : PAnsiChar; var Pos : Cardinal) : Boolean; register;
  485.   {- Case-insensitive search of Buffer for MatchString. Return indicates
  486.      success or failure.  Assumes MatchString is already raised to
  487.      uppercase (PRIOR to creating the table) -}
  488. var
  489.   BufPtr : Pointer;
  490. asm
  491.   push  edi                 { Save registers since we will be changing }
  492.   push  esi
  493.   push  ebx
  494.   push  edx
  495.  
  496.   mov   BufPtr, eax         { Copy Buffer to local variable and ESI }
  497.   mov   esi, eax
  498.   mov   ebx, ecx            { Copy BufLength to EBX }
  499.  
  500.   cld                       { Ensure forward string ops }
  501.   xor   eax, eax            { Zero out EAX so we can search for null }
  502.   mov   edi, MatchString    { Set EDI to beginning of MatchString }
  503.   or    ecx, -1             { We will be counting down }
  504.   repne scasb               { Find null }
  505.   not   ecx                 { ECX = length of MatchString + null }
  506.   dec   ecx                 { ECX = length of MatchString }
  507.   mov   edx, ecx            { Copy length of MatchString to EDX }
  508.  
  509.   pop   ecx                 { Pop length of buffer into ECX }
  510.   mov   edi, esi            { Set EDI to beginning of search buffer }
  511.   mov   esi, MatchString    { Set ESI to beginning of MatchString }
  512.  
  513.   or    dl, dl              { Check to see if we have a trivial case }
  514.   jz    @@BMSNotFound       { If Length(MatchString) = 0 we're done }
  515.  
  516. @@BMSInit:
  517.   dec   edx                 { Set up for BM Search }
  518.   add   esi, edx            { Set ESI to end of MatchString }
  519.   add   ecx, edi            { Set ECX to end of buffer }
  520.   add   edi, edx            { Set EDI to first check point }
  521.   mov   dh, [esi]           { Set DH to character we'll be looking for }
  522.   dec   esi                 { Dec ESI in prep for BMSFound loop }
  523.   std                       { Backward string ops }
  524.   jmp   @@BMSComp           { Jump to first comparison }
  525.  
  526. @@BMSNext:
  527.   mov   al, [ebx+eax]       { Look up skip distance from table }
  528.   add   edi, eax            { Skip EDI ahead to next check point }
  529.  
  530. @@BMSComp:
  531.   cmp   edi, ecx            { Have we reached end of buffer? }
  532.   jae   @@BMSNotFound       { If so, we're done }
  533.   mov   al, [edi]           { Move character from buffer into AL for comparison }
  534.  
  535.   push  ebx                 { Save registers }
  536.   push  ecx
  537.   push  edx
  538.   push  eax                 { Push Char onto stack for CharUpper }
  539.   cld
  540.   call  CharUpper
  541.   std
  542.   pop   edx                 { Restore registers }
  543.   pop   ecx
  544.   pop   ebx
  545.  
  546.   cmp   dh, al              { Compare }
  547.   jne   @@BMSNext           { If not equal, go to next checkpoint }
  548.  
  549.   push  ecx                 { Save ECX }
  550.   dec   edi
  551.   xor   ecx, ecx            { Zero ECX }
  552.   mov   cl, dl              { Move Length(MatchString) to ECX }
  553.   jecxz @@BMSFound          { If ECX is zero, string is found }
  554.  
  555. @@StringComp:
  556.   mov   al, [edi]           { Get char from buffer }
  557.   dec   edi                 { Dec buffer index }
  558.  
  559.   push  ebx                 { Save registers }
  560.   push  ecx
  561.   push  edx
  562.   push  eax                 { Push Char onto stack for CharUpper }
  563.   cld
  564.   call  CharUpper
  565.   std
  566.   pop   edx                 { Restore registers }
  567.   pop   ecx
  568.   pop   ebx
  569.  
  570.   mov   ah, al              { Move buffer char to AH }
  571.   lodsb                     { Get MatchString char }
  572.   cmp   ah, al              { Compare }
  573.   loope @@StringComp        { OK?  Get next character }
  574.   je    @@BMSFound          { Matched! }
  575.  
  576.   xor   ah, ah              { Zero AH }
  577.   mov   al, dl              { Move Length(MatchString) to AL }
  578.   sub   al, cl              { Calculate offset that string didn't match }
  579.   add   esi, eax            { Move ESI back to end of MatchString }
  580.   add   edi, eax            { Move EDI to pre-string compare location }
  581.   inc   edi
  582.   mov   al, dh              { Move character back to AL }
  583.   pop   ecx                 { Restore ECX }
  584.   jmp   @@BMSNext           { Do another compare }
  585.  
  586. @@BMSFound:                 { EDI points to start of match }
  587.   mov   edx, BufPtr         { Move pointer to buffer into EDX }
  588.   sub   edi, edx            { Calculate position of match }
  589.   mov   eax, edi
  590.   inc   eax
  591.   mov   esi, Pos
  592.   mov   [esi], eax          { Set Pos to position of match }
  593.   mov   eax, 1              { Set result to True }
  594.   pop   ecx                 { Restore ESP }
  595.   jmp   @@BMSDone
  596.  
  597. @@BMSNotFound:
  598.   xor   eax, eax            { Set result to False }
  599.  
  600. @@BMSDone:
  601.   cld                       { Restore direction flag }
  602.   pop   ebx                 { Restore registers }
  603.   pop   esi
  604.   pop   edi
  605. end;
  606. {$ELSE}
  607. function BMSearchUC(var Buffer;
  608.                     BufLength : Cardinal;
  609.                     var BT : BTable;
  610.                     MatchString : PAnsiChar;
  611.                     var Pos : Cardinal) : Boolean; assembler;
  612.   {-Case-insensitive search of Buffer for MatchString
  613.     Return indicates success or failure
  614.     Assumes MatchString is already raised to uppercase}
  615. asm
  616.   PUSH   DS                       {Will wipe out DS}
  617.   PUSH   BP                       {Will use BP for temp storage later}
  618.   XOR    AX,AX
  619.   XOR    DX,DX
  620.   CLD
  621.   LES    DI,MatchString
  622.   MOV    CX,0FFFFh
  623.   REPNE  SCASB
  624.   NOT    CX
  625.   DEC    CX
  626.   MOV    DL,CL
  627.  
  628.   MOV    CX,BufLength             {CX = Buffer size}
  629.   LES    DI,Buffer                {ES:DI => Buffer}
  630.   LDS    BX,BT                    {DS:BX => BTable}
  631.   MOV    AX,DS                    {Keep BTable segment in AX a moment}
  632.   LDS    SI,MatchString           {DS:SI => MatchString}
  633.   MOV    BP,AX                    {Keep BTable segment in BP}
  634.  
  635.   XOR    AX,AX                    {AX = 0}
  636.   OR     DL,DL                    {Check for trivial case}
  637.   JZ     @@BMSUnotFound           {Fail for empty string}
  638.  
  639. @@BMSUinit:
  640.   DEC    DL                       {DX = length(MatchString)-1}
  641.   ADD    SI,DX                    {DS:SI => MatchString[length(MatchString)-1]}
  642.   ADD    CX,DI                    {CX = offset of last char in buffer}
  643.   ADD    DI,DX                    {ES:DI => first position to search}
  644.   MOV    DH,[SI]                  {DH = MatchString[length(MatchString)]}
  645.   DEC    SI
  646.   STD                             {Go backwards}
  647.   JMP    @@BMSUcomp               {Skip link table first time}
  648.  
  649. @@BMSUnext:
  650.   PUSH   DS                       {Save DS a moment}
  651.   MOV    DS,BP                    {Get segment of link table}
  652.   XLAT                            {Get size of link at DS:[BX+AL]}
  653.   POP    DS                       {Restore DS}
  654.   ADD    DI,AX                    {Compute next place to search}
  655.  
  656. @@BMSUcomp:
  657.   JC     @@BMSUnotFound           {Done if overflowed 64K}
  658.   CMP    DI,CX                    {At end of buffer?}
  659.   JAE    @@BMSUnotFound           {Done if so}
  660.   MOV    AL,ES:[DI]               {AL = next char to try}
  661.   cld
  662.   CALL   UpCasePrim               {Raise it to uppercase}
  663.   std
  664.   CMP    DH,AL                    {Does it match the end of MatchString?}
  665.   JNE    @@BMSUnext               {If not same, go back and try again}
  666.  
  667.   PUSH   CX                       {Save end of buffer position}
  668.   DEC    DI                       {Start comparing one character before}
  669.   MOV    CL,DL                    {Compare length(MatchString)-1 characters}
  670.   MOV    CH,AH                    {CH = 0}
  671.   JCXZ   @@BMSUfound              {Completely matched if CX = 0}
  672.  
  673. @@BMSUcomp2:
  674.   LODSB                           {Next match character in AL}
  675.   MOV    AH,ES:[DI]               {Next buffer character in AH}
  676.   DEC    DI                       {Decrement buffer index}
  677.   XCHG   AL,AH                    {Uppercase it}
  678.   PUSH   BX
  679.   MOV    BL,AH
  680.   cld
  681.   CALL   UpCasePrim
  682.   std
  683.   MOV    AH,BL
  684.   POP    BX
  685.   CMP    AH,AL                    {A match?}
  686.   LOOPE  @@BMSUcomp2              {Loop while AH=AL and CX<>0}
  687.   JE     @@BMSUfound              {Matched!}
  688.  
  689.   XOR    AH,AH                    {Restore SI,DI,AX}
  690.   MOV    AL,DL
  691.   SUB    AL,CL
  692.   ADD    SI,AX
  693.   ADD    DI,AX
  694.   INC    DI
  695.   MOV    AL,DH                    {Put matched char back in AL}
  696.   POP    CX                       {Restore end of buffer}
  697.   JMP    @@BMSUnext               {Try again}
  698.  
  699. @@BMSUfound:                      {DI points to start of match}
  700.   INC    SP                       {End of buffer off stack}
  701.   INC    SP
  702.   POP    BP                       {Get frame pointer back}
  703.   SUB    DI,WORD PTR Buffer       {Subtract buffer start address}
  704.   MOV    AX,DI
  705.   INC    AX                       {Return 0 if found in first byte}
  706.   les    di,Pos
  707.   mov    es:[di],ax               {Set Pos}
  708.   mov    ax,1                     {Result = True}
  709.   JMP    @@BMSUDone2              {We're done}
  710.  
  711. @@BMSUnotFound:
  712.   {MOV    AX,0FFFFh}
  713.   xor    ax, ax                   {Result = False}
  714. @@BMSUDone:
  715.   POP    BP
  716. @@BMSUDone2:
  717.   CLD
  718.   POP    DS
  719. end;
  720. {$ENDIF}
  721.  
  722. {$IFDEF Win32}
  723. function CharStrPChar(Dest : PAnsiChar; C : AnsiChar;
  724.                       Len : Cardinal) : PAnsiChar; register;
  725. asm
  726.   push    edi            { Save EDI-about to change it }
  727.   push    eax            { Save Dest pointer for return }
  728.   mov     edi, eax       { Point EDI to Dest }
  729.  
  730.   mov     dh, dl         { Dup character 4 times }
  731.   mov     eax, edx
  732.   shl     eax, $10
  733.   mov     ax, dx
  734.  
  735.   mov     edx, ecx       { Save Len }
  736.  
  737.   cld                    { Forward! }
  738.   shr     ecx, 2         { Store dword char chunks first }
  739.   rep     stosd
  740.   mov     ecx, edx       { Store remaining characters }
  741.   and     ecx, 3
  742.   rep     stosb
  743.  
  744.   xor     al,al          { Add null terminator }
  745.   stosb
  746.  
  747.   pop     eax            { Return Dest pointer }
  748.   pop     edi            { Restore orig value of EDI }
  749. end;
  750. {$ELSE}
  751. function CharStrPChar(Dest : PAnsiChar; C : AnsiChar;
  752.                       Len : Cardinal) : PAnsiChar; assembler;
  753. asm
  754.   les     di,Dest
  755.   mov     si,di
  756.   mov     cx,Len
  757.   jcxz    @@ExitPoint
  758.   cld
  759.   mov     al,C
  760.   mov     ah,al
  761.   test    di,1
  762.   jz      @@WordAligned
  763.   stosb
  764.   dec     cx
  765. @@WordAligned:
  766.   shr     cx,1
  767.   rep     stosw
  768.   jnc     @@ExitPoint
  769.   stosb
  770. @@ExitPoint:
  771.   xor     al,al
  772.   stosb
  773.   mov     dx,es
  774.   mov     ax,si
  775. end;
  776. {$ENDIF}
  777.  
  778. {$IFDEF Win32}
  779. function DetabPChar(Dest : PAnsiChar; Src : PAnsiChar;
  780.                     TabSize : Byte) : PAnsiChar; register;
  781.   { -Expand tabs in a string to blanks on spacing TabSize- }
  782. asm
  783.   push    eax           { Save Dest for return value }
  784.   push    edi           { Save EDI, ESI and EBX, we'll be changing them }
  785.   push    esi
  786.   push    ebx
  787.  
  788.   mov     esi, edx      { ESI -> Src }
  789.   mov     edi, eax      { EDI -> Dest }
  790.   xor     ebx, ebx      { Get TabSize in EBX }
  791.   add     bl, cl
  792.   jz      @@Done        { Exit if TabSize is zero }
  793.  
  794.   cld                   { Forward! }
  795.   xor     edx, edx      { Set output length to zero }
  796.  
  797. @@Next:
  798.   lodsb                 { Get next input character }
  799.   or      al, al        { Is it a null? }
  800.   jz      @@Done        { Yes-all done }
  801.   cmp     al, 09        { Is it a tab? }
  802.   je      @@Tab         { Yes, compute next tab stop }
  803.   stosb                 { No, store to output }
  804.   inc     edx           { Increment output length }
  805.   jmp     @@Next        { Next character }
  806.  
  807. @@Tab:
  808.   push    edx           { Save output length }
  809.   mov     eax, edx      { Get current output length in DX:AX }
  810.   xor     edx, edx
  811.   div     ebx           { Output length MOD TabSize in DX }
  812.   mov     ecx, ebx      { Calc number of spaces to insert... }
  813.   sub     ecx, edx      { = TabSize - Mod value }
  814.   pop     edx
  815.   add     edx, ecx      { Add count of spaces into current output length }
  816.  
  817.   mov     eax,$2020     { Blank in AH, Blank in AL }
  818.   shr     ecx, 1        { Store blanks }
  819.   rep     stosw
  820.   adc     ecx, ecx
  821.   rep     stosb
  822.   jmp     @@Next        { Back for next input }
  823.  
  824. @@Done:
  825.   xor     al,al         { Store final null terminator }
  826.   stosb
  827.  
  828.   pop     ebx           { Restore caller's EBX, ESI and EDI }
  829.   pop     esi
  830.   pop     edi
  831.   pop     eax           { Return Dest }
  832. end;
  833. {$ELSE}
  834. function DetabPChar(Dest : PAnsiChar; Src : PAnsiChar;
  835.                     TabSize : Byte) : PAnsiChar; assembler;
  836.   {-Expand tabs in a string to blanks on spacing TabSize}
  837. asm
  838.   PUSH   DS
  839.   CLD
  840.   XOR    CX,CX                    {Default input length = 0}
  841.   XOR    DX,DX                    {Default output length = 0 in DL}
  842.   LDS    SI,Src                   {DS:SI => input string}
  843.   LES    DI,Dest                  {ES:DI => output string}
  844.   PUSH   ES                       {save ES:DI for function result}
  845.   PUSH   DI
  846.   XOR    BH,BH
  847.   MOV    BL,TabSize               {BX has tab size}
  848.   OR     BL,BL                    {Return zero length string if TabSize = 0}
  849.   JZ     @@Done
  850.  
  851.   MOV    AH,09                    {Store tab in AH}
  852.  
  853. @@Next:
  854.   LODSB                           {Next input character}
  855.   OR     AL,AL                    {Is it a null?}
  856.   JZ     @@Done
  857.   CMP    AL,AH                    {Is it a tab?}
  858.   JE     @@Tab                    {Yes, compute next tab stop}
  859.   STOSB                           {No, store to output}
  860.   INC    DX                       {Increment output length}
  861.   JMP    @@Next                   {Next character}
  862.  
  863. @@Tab:
  864.   PUSH   DX                       {Save output length}
  865.   MOV    AX,DX                    {Current output length in DX:AX}
  866.   XOR    DX,DX
  867.   DIV    BX                       {OLen DIV TabSize in AL}
  868.  
  869.   mov    cx, bx                   {Calc number of spaces to insert...}
  870.   sub    cx, dx                   { = TabSize - Mod value}
  871.   pop    dx
  872.   add    dx, cx                   {Add count of spaces into current output length}
  873.  
  874.   MOV    AX,$0920                 {Tab in AH, Blank in AL}
  875.   REP    STOSB                    {Store blanks}
  876.   JMP    @@Next                   {Back for next input}
  877.  
  878. @@Done:
  879.   XOR    AL,AL
  880.   STOSB
  881.   POP    AX                       {function result = Dest}
  882.   POP    DX
  883.   POP    DS
  884. end;
  885. {$ENDIF}
  886.  
  887. function HexBPChar(Dest : PAnsiChar; B : Byte) : PAnsiChar;
  888.   {-Return hex string for byte}
  889. begin
  890.   Result := Dest;
  891.   Dest^ := Digits[B shr 4];
  892.   Inc(Dest);
  893.   Dest^ := Digits[B and $F];
  894.   Inc(Dest);
  895.   Dest^ := #0;
  896. end;
  897.  
  898. function HexLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  899.   {-Return the hex string for a long integer}
  900. var
  901.   T2 : Array[0..4] of AnsiChar;
  902. begin
  903.   Result := StrCat(HexWPChar(Dest, HIWORD(L)), HexWPChar(T2, LOWORD(L)));
  904. end;
  905.  
  906. function HexPtrPChar(Dest : PAnsiChar; P : Pointer) : PAnsiChar;
  907.   {-Return hex string for pointer}
  908. var
  909.   T2 : Array[0..4] of AnsiChar;
  910. begin
  911.   StrCat(HexWPChar(Dest, HIWORD(LongInt(P))), ':');
  912.   Result := StrCat(Dest, HexWPChar(T2, LOWORD(LongInt(P))));
  913. end;
  914.  
  915. function HexWPChar(Dest : PAnsiChar; W : Word) : PAnsiChar;
  916. begin
  917.   Result := Dest;
  918.   Dest^ := Digits[Hi(W) shr 4];
  919.   Inc(Dest);
  920.   Dest^ := Digits[Hi(W) and $F];
  921.   Inc(Dest);
  922.   Dest^ := Digits[Lo(W) shr 4];
  923.   Inc(Dest);
  924.   Dest^ := Digits[Lo(W) and $F];
  925.   Inc(Dest);
  926.   Dest^ := #0;
  927. end;
  928.  
  929. {$IFDEF Win32}
  930. function LoCaseChar(C: AnsiChar) : AnsiChar; register;
  931. asm
  932.   mov   edx, eax
  933.   xor   eax, eax
  934.   mov   al, dl
  935.   push  eax
  936.   call  CharLower
  937. end;
  938. {$ELSE}
  939. procedure LoCasePrim; near; assembler;
  940.   {-Call the Windows AnsiLower function}
  941. asm
  942.   push    bx
  943.   push    cx
  944.   push    dx
  945.   push    si
  946.   push    di
  947.   push    es
  948.   push    ax
  949.   xor     ah,ah
  950.   xor     bx,bx
  951.   push    bx
  952.   push    ax
  953.   call    AnsiLower
  954.   pop     bx
  955.   mov     ah,bh
  956.   pop     es
  957.   pop     di
  958.   pop     si
  959.   pop     dx
  960.   pop     cx
  961.   pop     bx
  962. end;
  963.  
  964. function LoCaseChar(C : AnsiChar) : AnsiChar; assembler;
  965. asm
  966.   mov     al,C
  967.   call    LocasePrim
  968. end;
  969. {$ENDIF}
  970.  
  971. function OctalLPChar(Dest : PAnsiChar; L : LongInt) : PAnsiChar;
  972.   {-Return the octal PAnsiChar string for a long integer}
  973. var
  974.   I : LongInt;
  975. begin
  976.   Result := Dest;
  977.   FillChar(Dest^, 12, '0');
  978.   Dest[12] := #0;
  979.   for I := 11 downto 0 do begin
  980.     if L = 0 then
  981.       Exit;
  982.  
  983.     Dest[I] := Digits[L and 7];
  984.     L :=  L shr 3;
  985.   end;
  986. end;
  987.  
  988. {$IFDEF Win32}
  989. function StrChDeletePrim(P : PAnsiChar; Pos : Cardinal) : PAnsiChar; register;
  990. asm
  991.   push   edi             { Save because we will be changing them }
  992.   push   esi
  993.   push   ebx
  994.  
  995.   mov    ebx, eax        { Save P to EDI & EBX }
  996.   mov    edi, eax
  997.  
  998.   xor    al, al          { Zero }
  999.   or     ecx, -1         { Set ECX to $FFFFFFFF }
  1000.   cld
  1001.   repne  scasb           { Find null terminator }
  1002.   not    ecx
  1003.   jecxz  @@ExitPoint
  1004.   sub    ecx, edx        { Calc number to move }
  1005.   jb     @@ExitPoint     { Exit if Pos > StrLen }
  1006.  
  1007.   mov    edi, ebx
  1008.   add    edi, edx        { Point to position to adjust }
  1009.   mov    esi, edi
  1010.   inc    esi             { Offset for source string }
  1011.   inc    ecx             { One more to include null terminator }
  1012.   rep    movsb           { Adjust the string }
  1013. @@ExitPoint:
  1014.  
  1015.   mov    eax, ebx
  1016.   pop    ebx             { restore registers }
  1017.   pop    esi
  1018.   pop    edi
  1019. end;
  1020. {$ELSE}
  1021. function StrChDeletePrim(P : PAnsiChar; Pos : Cardinal) : PAnsiChar; assembler;
  1022. asm
  1023.   push      ds
  1024.   mov       bx,Pos
  1025.   inc       bx
  1026.   les       di,P
  1027.   mov       dx,di
  1028.   xor       al,al
  1029.   mov       cx,0FFFFh
  1030.   cld
  1031.   repne     scasb                    {find null terminator}
  1032.   not       cx
  1033.   dec       cx                       {calc length}
  1034.   jcxz      @@ExitPoint
  1035.   sub       cx,bx                    {calc number to move}
  1036.   jb        @@ExitPoint              {exit if Pos > StrLen}
  1037.   mov       di,dx
  1038.   add       di,bx                    {point to position to adjust}
  1039.   mov       si,di
  1040.   dec       di
  1041.   mov       ax,es
  1042.   mov       ds,ax
  1043.   inc       cx                       {one more to include null terminator}
  1044.   rep       movsb                    {adjust the string}
  1045. @@ExitPoint:
  1046.   pop       ds
  1047.   les       ax,P
  1048.   mov       dx,es
  1049. end;
  1050. {$ENDIF}
  1051.  
  1052. {$IFDEF Win32}
  1053. function StrChInsertPrim(Dest : PAnsiChar; C : AnsiChar;
  1054.                          Pos : Cardinal) : PAnsiChar; register;
  1055. asm
  1056.   push   eax             {save because we will be changing them}
  1057.   push   edi
  1058.   push   esi
  1059.   push   ebx
  1060.  
  1061.   xor    ebx, ebx        {zero}
  1062.   mov    ebx, ecx        {move POS to ebx}
  1063.  
  1064.   mov    esi, eax        {copy Dest to ESI and EDI}
  1065.   mov    edi, eax
  1066.  
  1067.   xor    al, al          {zero}
  1068.   or     ecx, -1         {set ECX to $FFFFFFFF}
  1069.   cld                    {ensure forward}
  1070.   repne  scasb           {find null terminator}
  1071.  
  1072.   not    ecx             {calc length (including null)}
  1073.   std                    {backwards string ops}
  1074.   add    esi, ecx
  1075.   dec    esi             {point to end of source string}
  1076.   sub    ecx, ebx        {calculate number to do}
  1077.   jae    @@1             {append if Pos greater than strlen + 1}
  1078.   mov    ecx, 1
  1079.  
  1080. @@1:
  1081.   rep    movsb           {adjust tail of string}
  1082.   mov    eax, edx
  1083.   stosb                  {insert the new character}
  1084.  
  1085. @@ExitPoint:
  1086.  
  1087.   cld                    {be a good neighbor}
  1088.   pop    ebx             {restore registers}
  1089.   pop    esi
  1090.   pop    edi
  1091.   pop    eax
  1092. end;
  1093. {$ELSE}
  1094. function StrChInsertPrim(Dest : PAnsiChar; C : AnsiChar;
  1095.                          Pos : Cardinal) : PAnsiChar; assembler;
  1096. asm
  1097.   push      ds
  1098.   mov       bx,Pos
  1099.   lds       si,Dest
  1100.   les       di,Dest
  1101.   mov       dx,di
  1102.   xor       al,al
  1103.   mov       cx,0FFFFh
  1104.   cld
  1105.   repne     scasb                    {find null terminator}
  1106.   not       cx                       {calc length (including null)}
  1107.   std                                {backwards string ops}
  1108.   add       si,cx
  1109.   dec       si                       {point to end of source string}
  1110.   mov       dx,cx
  1111.   sub       cx,bx                    {calculate number to do}
  1112.   jae       @@1                      {exit if Pos greater than strlen + 1}
  1113.   mov       cx,1
  1114.  
  1115. @@1:
  1116.   rep       movsb                    {adjust tail of string}
  1117.   mov       al,C
  1118.   stosb                              {insert the new character}
  1119.  
  1120. @@ExitPoint:
  1121.   cld
  1122.   pop       ds
  1123.   les       ax,Dest
  1124.   mov       dx,es
  1125. end;
  1126. {$ENDIF}
  1127.  
  1128. {$IFDEF Win32}
  1129.  
  1130. function StrChPos(P : PAnsiChar; C : AnsiChar;
  1131.                   var Pos : Cardinal): Boolean; register;
  1132.   {-Sets Pos to position of character C within string P returns True if found}
  1133. asm
  1134.   push   esi               {save since we'll be changing}
  1135.   push   edi
  1136.   push   ebx
  1137.   mov    esi, ecx          {save Pos}
  1138.  
  1139.   cld                      {forward string ops}
  1140.   mov    edi, eax          {copy P to EDI}
  1141.   or     ecx, -1
  1142.   xor    eax, eax          {zero}
  1143.   mov    ebx, edi          {save EDI to EBX}
  1144.   repne  scasb             {search for NULL terminator}
  1145.   not    ecx
  1146.   dec    ecx               {ecx has len of string}
  1147.  
  1148.   test   ecx, ecx
  1149.   jz     @@NotFound        {if len of P = 0 then done}
  1150.  
  1151.   mov    edi, ebx          {reset EDI to beginning of string}
  1152.   mov    al, dl            {copy C to AL}
  1153.   repne  scasb             {find C in string}
  1154.   jne    @@NotFound
  1155.  
  1156.   mov    ecx, edi          {calculate position of C}
  1157.   sub    ecx, ebx
  1158.   dec    ecx               {ecx holds found position}
  1159.  
  1160.   mov    [esi], ecx        {store location}
  1161.   mov    eax, 1            {return true}
  1162.   jmp    @@ExitCode
  1163.  
  1164. @@NotFound:
  1165.   xor    eax, eax
  1166.  
  1167. @@ExitCode:
  1168.  
  1169.   pop    ebx               {restore registers}
  1170.   pop    edi
  1171.   pop    esi
  1172. end;
  1173.  
  1174. {$ELSE}
  1175.  
  1176. function StrChPos(P : PAnsiChar; C : AnsiChar;
  1177.                   var Pos : Cardinal): Boolean; assembler;
  1178.   {-Sets Pos to position of character C within string P returns True if found}
  1179. asm
  1180.   cld
  1181.   les       di,P
  1182.   xor       al,al
  1183.   mov       cx,0FFFFh
  1184.   mov       bx,di
  1185.   repne     scasb
  1186.   not       cx
  1187.   dec       cx                       {cx has len of P}
  1188.   jcxz      @@ExitCode
  1189.   mov       di,bx
  1190.   mov       al,C
  1191.   repne     scasb                    {find C in P}
  1192.   jne       @@ExitCode
  1193.   mov       cx,di
  1194.   sub       cx,bx
  1195.   dec       cx
  1196.   les       di,Pos
  1197.   mov       es:[di],cx
  1198.   mov       cx,1
  1199. @@ExitCode:
  1200.   mov       ax,cx
  1201.   {dec       ax}
  1202. end;
  1203. {$ENDIF}
  1204.  
  1205. procedure StrInsertChars(Dest : PAnsiChar; Ch : AnsiChar; Pos, Count : Word);
  1206.   {-Insert count instances of Ch into S at Pos}
  1207. var
  1208.   A : array[0..1024] of AnsiChar;
  1209. begin
  1210.   FillChar(A, Count, Ch);
  1211.   A[Count] := #0;
  1212.   StrStInsertPrim(Dest, A, Pos);
  1213. end;
  1214.  
  1215. function StrStCopy(Dest : PAnsiChar; S : PAnsiChar; Pos, Count : Cardinal) : PAnsiChar;
  1216. var
  1217.   Len : Cardinal;
  1218. begin
  1219.   Len := StrLen(S);
  1220.   if Pos < Len then begin
  1221.     if (Len-Pos) < Count then
  1222.       Count := Len-Pos;
  1223.     Move(S[Pos], Dest^, Count);
  1224.     Dest[Count] := #0;
  1225.   end else
  1226.     Dest[0] := #0;
  1227.   Result := Dest;
  1228. end;
  1229.  
  1230. {$IFDEF Win32}
  1231. function StrStDeletePrim(P : PAnsiChar; Pos, Count : Cardinal) : PAnsiChar; register;
  1232. asm
  1233.   push   eax             {save because we will be changing them}
  1234.   push   edi
  1235.   push   esi
  1236.   push   ebx
  1237.  
  1238.   mov    ebx, ecx        {move Count to BX}
  1239.   mov    esi, eax        {move P to ESI and EDI}
  1240.   mov    edi, eax
  1241.  
  1242.   xor    eax, eax        {null}
  1243.   or     ecx, -1
  1244.   cld
  1245.   repne  scasb           {find null terminator}
  1246.   not    ecx             {calc length}
  1247.   jecxz  @@ExitPoint
  1248.  
  1249.   sub    ecx, ebx        {subtract Count}
  1250.   sub    ecx, edx        {subtract Pos}
  1251.   jns    @@L1
  1252.  
  1253.   mov    edi,esi         {delete everything after Pos}
  1254.   add    edi,edx
  1255.   stosb
  1256.   jmp    @@ExitPoint
  1257.  
  1258. @@L1:
  1259.   mov    edi,esi
  1260.   add    edi,edx         {point to position to adjust}
  1261.   mov    esi,edi
  1262.   add    esi,ebx         {point past string to delete in src}
  1263.   inc    ecx             {one more to include null terminator}
  1264.   rep    movsb           {adjust the string}
  1265.  
  1266. @@ExitPoint:
  1267.  
  1268.   pop    ebx            {restore registers}
  1269.   pop    esi
  1270.   pop    edi
  1271.   pop    eax
  1272. end;
  1273. {$ELSE}
  1274. function StrStDeletePrim(P : PAnsiChar; Pos, Count : Cardinal) : PAnsiChar; assembler;
  1275. asm
  1276.   push      ds
  1277.   mov       bx,Pos
  1278.   inc       bx
  1279.   les       di,P
  1280.   mov       dx,di
  1281.   xor       al,al
  1282.   mov       cx,0FFFFh
  1283.   cld
  1284.   repne     scasb                    {find null terminator}
  1285.   not       cx
  1286.   dec       cx                       {calc length}
  1287.   inc       cx
  1288.   jcxz      @@ExitPoint
  1289.   sub       cx,Count                 {calc number to move}
  1290.   sub       cx,bx
  1291.   jns       @@L1                     {delete string in middle}
  1292.  
  1293.   mov       di,dx                    {delete everything after Pos}
  1294.   add       di,bx
  1295.   dec       di
  1296.   xor       al,al
  1297.   stosb
  1298.   jmp       @@ExitPoint
  1299. @@L1:
  1300.   mov       di,dx
  1301.   add       di,bx                    {point to position to adjust}
  1302.   dec       di
  1303.   mov       si,di
  1304.   add       si,Count                 {point past string to delete in src}
  1305.   mov       ax,es
  1306.   mov       ds,ax
  1307.   inc       cx                       {one more to include null terminator}
  1308.   rep       movsb                    {adjust the string}
  1309. @@ExitPoint:
  1310.   pop       ds
  1311.   les       ax,P
  1312.   mov       dx,es
  1313. end;
  1314. {$ENDIF}
  1315.  
  1316. function StrStInsert(Dest : PAnsiChar; S1, S2 : PAnsiChar; Pos : Cardinal) : PAnsiChar;
  1317. begin
  1318.   StrCopy(Dest, S1);
  1319.   Result := StrStInsertPrim(Dest, S2, Pos);
  1320. end;
  1321.  
  1322. {$IFDEF Win32}
  1323. function StrStInsertPrim(Dest : PAnsiChar; S : PAnsiChar;
  1324.                          Pos : Cardinal) : PAnsiChar; register;
  1325. asm
  1326.   push   eax             {save because we will be changing them}
  1327.   push   edi
  1328.   push   esi
  1329.   push   ebx
  1330.  
  1331.   mov    ebx, ecx        {move POS to ebx}
  1332.   mov    esi, eax        {copy Dest to ESI, S to EDI}
  1333.   mov    edi, edx
  1334.  
  1335.   xor    al, al          {zero}
  1336.   or     ecx, -1         {set ECX to $FFFFFFFF}
  1337.   cld                    {ensure forward}
  1338.   repne  scasb           {find null terminator}
  1339.   not    ecx             {calc length of source string (including null)}
  1340.   dec    ecx             {length without null}
  1341.   jecxz  @@ExitPoint     {if source length = 0, exit}
  1342.   push   ecx             {save length for later}
  1343.  
  1344.   mov    edi, esi        {reset EDI to Dest}
  1345.   or     ecx, -1
  1346.   repne  scasb           {find null}
  1347.   not    ecx             {length of dest string}
  1348.  
  1349.   cmp    ebx, ecx
  1350.   jb     @@1
  1351.   mov    ebx, ecx
  1352.   dec    ebx
  1353.  
  1354. @@1:
  1355.   std                    {backwards string ops}
  1356.   pop    eax             {restore length of S from stack}
  1357.   add    edi, eax        {set EDI S beyond end of Dest}
  1358.   dec    edi             {back up one for null}
  1359.  
  1360.   add    esi, ecx        {set ESI to end of Dest}
  1361.   dec    esi             {back up one for null}
  1362.   sub    ecx, ebx        {# of chars in Dest that are past Pos}
  1363.   rep    movsb           {adjust tail of string}
  1364.  
  1365.   mov    esi, edx        {set ESI to S}
  1366.   add    esi, eax        {set ESI to end of S}
  1367.   dec    esi             {back up one for null}
  1368.   mov    ecx, eax        {# of chars in S}
  1369.   rep    movsb           {copy S into Dest}
  1370.  
  1371.   cld                    {be a good neighbor}
  1372.  
  1373. @@ExitPoint:
  1374.   pop    ebx             {restore registers}
  1375.   pop    esi
  1376.   pop    edi
  1377.   pop    eax
  1378. end;
  1379. {$ELSE}
  1380. function StrStInsertPrim(Dest : PAnsiChar; S : PAnsiChar;
  1381.                          Pos : Cardinal) : PAnsiChar; assembler;
  1382. asm
  1383.   cld
  1384.   xor       al,al
  1385.   les       di,S
  1386.   mov       cx,0FFFFh
  1387.   repne     scasb
  1388.   not       cx
  1389.   mov       dx,cx
  1390.   dec       dx                       {dx = StrLen(S)}
  1391.   or        dx,dx                    {exit if StrLen(S) = 0}
  1392.   je        @@ExitPoint
  1393.  
  1394.   les       di,Dest
  1395.   mov       cx,0FFFFh
  1396.   repne     scasb
  1397.   not       cx                       {cx = StrLen(Dest)+1}
  1398.  
  1399.   mov       bx,Pos                   {bx = Pos}
  1400.   cmp       bx,cx                    {append if Pos > StrLen(Dest)}
  1401.   jb        @@1
  1402.   mov       bx,cx
  1403.   dec       bx                       {bx = StrLen(Dest)}
  1404.  
  1405. @@1:
  1406.   push      ds
  1407.   std                                {backwards string ops}
  1408.   add       di,dx
  1409.   dec       di                       {es:di -> past end of Dest+S}
  1410.   lds       si,Dest
  1411.   add       si,cx
  1412.   dec       si                       {ds:si -> past end of Dest}
  1413.   sub       cx,bx                    {# of chars in Dest that are past Pos}
  1414.   rep       movsb                    {adjust tail of string}
  1415.   lds       si,S
  1416.   add       si,dx
  1417.   dec       si                       {ds:si -> past end of S}
  1418.   mov       cx,dx                    {# of chars in S}
  1419.   rep       movsb                    {copy S into Dest}
  1420.   cld                                {return to forwards for safety}
  1421.   pop       ds
  1422.  
  1423. @@ExitPoint:
  1424.   les       ax,Dest
  1425.   mov       dx,es
  1426. end;
  1427. {$ENDIF}
  1428.  
  1429. {$IFDEF Win32}
  1430. function StrStPos(P, S : PAnsiChar; var Pos : Cardinal) : boolean; register;
  1431. asm
  1432.   push   edi                 { Save registers }
  1433.   push   esi
  1434.   push   ebx
  1435.   push   ecx
  1436.  
  1437.   mov    ebx, eax            { Move P to EBX }
  1438.   mov    edi, edx            { Move S to EDI & ESI }
  1439.   mov    esi, edx
  1440.  
  1441.   xor    eax, eax            { Zero EAX }
  1442.   or     ecx, -1             { Set ECX to FFFFFFFF }
  1443.   repne  scasb               { Find null at end of S }
  1444.   not    ecx
  1445.  
  1446.   mov    edx, ecx            { Save length to EDX }
  1447.   dec    edx                 { EDX has len of S }
  1448.   test   edx, edx
  1449.   jz     @@NotFound          { If len of S = 0 then done }
  1450.  
  1451.   mov    edi, ebx            { Set EDI to beginning of P }
  1452.   or     ecx, -1             { Set ECX to FFFFFFFF }
  1453.   repne  scasb               { Find null at end of P }
  1454.   not    ecx
  1455.   dec    ecx                 { ECX has len of P }
  1456.   jcxz   @@NotFound          { If len of P = 0 then done }
  1457.  
  1458.   dec    edx
  1459.   sub    ecx,edx             { Max chars to search }
  1460.   jbe    @@NotFound          { Done if len S > len P }
  1461.   lodsb                      { Get first char of S in AL }
  1462.   mov    edi,ebx             { Set EDI to beginning of EDI }
  1463.  
  1464. @@Next:
  1465.   repne  scasb               { Find first char of S in P }
  1466.   jne    @@NotFound          { If not found then done }
  1467.   test   edx, edx            { If length of S was one then found }
  1468.   jz     @@Found
  1469.   push   ecx
  1470.   push   edi
  1471.   push   esi
  1472.   mov    ecx,edx
  1473.   repe   cmpsb               { See if remaining chars in S match }
  1474.   pop    esi
  1475.   pop    edi
  1476.   pop    ecx
  1477.   je     @@Found             { Yes, so found }
  1478.   jmp    @@Next              { Look for next first char occurrence }
  1479.  
  1480. @@NotFound:
  1481.   pop    ecx
  1482.   xor    eax,eax             { Set return to False }
  1483.   jmp    @@ExitPoint
  1484.  
  1485. @@Found:
  1486.   dec    edi                 { Calc position of found string }
  1487.   mov    eax, edi
  1488.   sub    eax, ebx
  1489.   pop    ecx
  1490.   mov    [ecx], eax
  1491.   mov    eax, 1              { Set return to True }
  1492.  
  1493. @@ExitPoint:
  1494.   pop    ebx                 { Restore registers }
  1495.   pop    esi
  1496.   pop    edi
  1497. end;
  1498. {$ELSE}
  1499. function StrStPos(P, S : PAnsiChar; var Pos : Cardinal) : boolean; assembler;
  1500. asm
  1501.   push      ds
  1502.   cld
  1503.   les       di,S
  1504.   mov       si,di
  1505.   mov       bx,es
  1506.   mov       ds,bx
  1507.   xor       ax,ax
  1508.   mov       cx,0FFFFh
  1509.   mov       bx,di
  1510.   repne     scasb
  1511.   not       cx
  1512.   mov       dx,cx
  1513.   dec       dx                       {dx has len of S}
  1514.   test      dx,dx
  1515.   jz        @@ExitPoint              {if len of S = 0 then done}
  1516.   les       di,P
  1517.   mov       cx,0FFFFh
  1518.   mov       bx,di
  1519.   repne     scasb
  1520.   not       cx
  1521.   dec       cx                       {cx has len of P}
  1522.   jcxz      @@ExitPoint              {if len of P = 0 then done}
  1523.   dec       dx
  1524.   sub       cx,dx                    {max chars to search}
  1525.   jbe       @@ExitPoint              {done if len S > len P}
  1526.   lodsb                              {get first char of S in al}
  1527.   mov       di,bx
  1528. @@Next:
  1529.   repne     scasb                    {find first char of S in P}
  1530.   jne       @@NotFound               {if not found then done}
  1531.   test      dx,dx                    {if length of S was one then found}
  1532.   jz        @@Found
  1533.   push      cx
  1534.   push      di
  1535.   push      si
  1536.   mov       cx,dx
  1537.   repe      cmpsb                    {see if remaining chars in S match}
  1538.   pop       si
  1539.   pop       di
  1540.   pop       cx
  1541.   je        @@Found                  {yes, so found}
  1542.   jmp       @@Next                   {look for next first char occurance}
  1543. @@NotFound:
  1544.   xor       ax,ax                    {set return to false}
  1545.   jmp       @@ExitPoint
  1546. @@Found:
  1547.   dec       di
  1548.   mov       ax,di
  1549.   sub       ax,bx
  1550.   les       di, Pos
  1551.   mov       es:[di], ax
  1552.   mov       ax, 1                    {set return to true}
  1553.   {inc       ax}
  1554. @@ExitPoint:
  1555.   pop       ds
  1556.   {dec       ax}
  1557. end;
  1558. {$ENDIF}
  1559.  
  1560. function StrToLongPChar(S : PAnsiChar; var I : LongInt) : Boolean;
  1561.   {-Convert a string to a longint, returning true if successful}
  1562. var
  1563.   Code : Cardinal;
  1564.   P    : array[0..255] of AnsiChar;
  1565. begin
  1566.   if StrLen(S)+1 > SizeOf(P) then begin
  1567.     Result := False;
  1568.     I := -1;
  1569.     Exit;
  1570.   end;
  1571.   StrCopy(P, S);
  1572.   TrimTrailPrimPChar(P);
  1573.   if StrStPos(P, '0x', Code) then begin
  1574.     StrStDeletePrim(P, Code, 2);
  1575.     StrChInsertPrim(P, '$', Code);
  1576.   end;
  1577.   Val(P, I, Code);
  1578.   if Code <> 0 then begin
  1579.     I := Code - 1;
  1580.     Result := False;
  1581.   end else
  1582.     Result := True;
  1583. end;
  1584.  
  1585. {$IFNDEF Win32}
  1586. function StringOfChar(C : Char; Len : Cardinal) : string;
  1587. begin
  1588.   if Len = 0 then
  1589.     Result[0] := #0
  1590.   else begin
  1591.     Result[0] := Chr(Len);
  1592.     FillChar(Result[1], Len, C);
  1593.   end;
  1594. end;
  1595.  
  1596. function Trim(const S : string) : string;
  1597.   {-return a string with leading and trailing white space removed}
  1598. var
  1599.   I    : Word;
  1600.   SLen : Byte absolute Result;
  1601. begin
  1602.   Result := S;
  1603.   while (SLen > 0) and (Result[SLen] <= ' ') do
  1604.     Dec(SLen);
  1605.  
  1606.   I := 1;
  1607.   while (I <= SLen) and (Result[I] <= ' ') do
  1608.     Inc(I);
  1609.   Dec(I);
  1610.   if I > 0 then
  1611.     Delete(Result, 1, I);
  1612. end;
  1613. {$ENDIF}
  1614.  
  1615. procedure TrimAllSpacesPChar(P : PAnsiChar);
  1616.   {-Trim leading and trailing blanks from P}
  1617. var
  1618.   I : Integer;
  1619.   PT : PAnsiChar;
  1620. begin
  1621.   I := StrLen(P);
  1622.   if I = 0 then
  1623.     Exit;
  1624.  
  1625.   {delete trailing spaces}
  1626.   Dec(I);
  1627.   while (I >= 0) and (P[I] = ' ') do begin
  1628.     P[I] := #0;
  1629.     Dec(I);
  1630.   end;
  1631.  
  1632.   {delete leading spaces}
  1633.   I := 0;
  1634.   PT := P;
  1635.   while PT^ = ' ' do begin
  1636.     Inc(I);
  1637.     Inc(PT);
  1638.   end;
  1639.   if I > 0 then
  1640.     StrStDeletePrim(P, 0, I);
  1641. end;
  1642.  
  1643. function TrimEmbeddedZeros(const S : string) : string;
  1644.   {-trim embedded zeros from a numeric string in exponential format}
  1645. var
  1646.   I, J : Integer;
  1647. begin
  1648.   I := Pos('E', S);
  1649.   if I = 0 then
  1650.     Exit;  {nothing to do}
  1651.  
  1652.   Result := S;
  1653.  
  1654.   {get rid of excess 0's after the decimal point}
  1655.   J := I;
  1656.   while (J > 1) and (Result[J-1] = '0') do
  1657.     Dec(J);
  1658.   if J <> I then begin
  1659.     System.Delete(Result, J, I-J);
  1660.  
  1661.     {get rid of the decimal point if that's all that's left}
  1662.     if (J > 1) and (Result[J-1] = '.') then
  1663.       System.Delete(Result, J-1, 1);
  1664.   end;
  1665.  
  1666.   {get rid of excess 0's in the exponent}
  1667.   I := Pos('E', Result);
  1668.   if I > 0 then begin
  1669.     Inc(I);
  1670.     J := I;
  1671.     while Result[J+1] = '0' do
  1672.       Inc(J);
  1673.     if J > I then
  1674.       System.Delete(Result, I+1, J-I);
  1675.   end;
  1676. end;
  1677.  
  1678. procedure TrimEmbeddedZerosPChar(P : PAnsiChar);
  1679.   {-Trim embedded zeros from a numeric string in exponential format}
  1680. var
  1681.   I, J : Cardinal;
  1682. begin
  1683.   if not StrChPos(P, 'E', I) then
  1684.     Exit;
  1685.  
  1686.   {get rid of excess 0's after the decimal point}
  1687.   J := I;
  1688.   while (J > 0) and (P[J-1] = '0') do
  1689.     Dec(J);
  1690.   if J <> I then begin
  1691.     StrStDeletePrim(P, J, I-J);
  1692.  
  1693.     {get rid of the decimal point if that's all that's left}
  1694.     if (J > 0) and (P[J-1] = '.') then
  1695.       StrStDeletePrim(P, J-1, 1);
  1696.   end;
  1697.  
  1698.   {Get rid of excess 0's in the exponent}
  1699.   if StrChPos(P, 'E', I) then begin
  1700.     Inc(I);
  1701.     J := I;
  1702.     while P[J+1] = '0' do
  1703.       Inc(J);
  1704.     if J > I then
  1705.       if P[J+1] = #0 then
  1706.         P[I-1] := #0
  1707.       else
  1708.         StrStDeletePrim(P, I+1, J-I);
  1709.   end;
  1710. end;
  1711.  
  1712. function TrimTrailingZeros(const S : string) : string;
  1713.   {-Trim trailing zeros from a numeric string. It is assumed that there is
  1714.     a decimal point prior to the zeros. Also strips leading spaces.}
  1715. var
  1716.   I : Integer;
  1717. begin
  1718.   if S = '' then
  1719.     Exit;
  1720.  
  1721.   Result := S;
  1722.   I := Length(Result);
  1723.   {delete trailing zeros}
  1724.   while (Result[I] = '0') and (I > 1) do
  1725.     Dec(I);
  1726.   {delete decimal point, if any}
  1727.   if Result[I] = '.' then
  1728.     Dec(I);
  1729.   Result := Trim(Copy(Result, 1, I));
  1730. end;
  1731.  
  1732. procedure TrimTrailingZerosPChar(P : PAnsiChar);
  1733.   {-Trim trailing zeros from a numeric string. It is assumed that there is
  1734.     a decimal point prior to the zeros. Also strips leading spaces.}
  1735. var
  1736.   PT : PAnsiChar;
  1737. begin
  1738.   PT := StrEnd(P);
  1739.   if Pointer(PT) = Pointer(P) then
  1740.     Exit;
  1741.  
  1742.   {back up to character prior to null}
  1743.   Dec(PT);
  1744.  
  1745.   {delete trailing zeros}
  1746.   while PT^ = '0' do begin
  1747.     PT^ := #0;
  1748.     Dec(PT);
  1749.   end;
  1750.  
  1751.   {delete decimal point, if any}
  1752.   if PT^ = '.' then
  1753.     PT^ := #0;
  1754.  
  1755.   TrimAllSpacesPChar(P);
  1756. end;
  1757.  
  1758. {$IFDEF Win32}
  1759. function TrimTrailPrimPChar(S : PAnsiChar) : PAnsiChar; register;
  1760. asm
  1761.    cld
  1762.    push   edi
  1763.    mov    edx, eax
  1764.    mov    edi, eax
  1765.  
  1766.    or     ecx, -1
  1767.    xor    al, al
  1768.    repne  scasb
  1769.    not    ecx
  1770.    dec    ecx
  1771.    jecxz  @@ExitPoint
  1772.  
  1773.    dec    edi
  1774.  
  1775. @@1:
  1776.    dec    edi
  1777.    cmp    byte ptr [edi],' '
  1778.    jbe    @@1
  1779.    mov    byte ptr [edi+1],00h
  1780. @@ExitPoint:
  1781.    mov    eax, edx
  1782.    pop    edi
  1783. end;
  1784. {$ELSE}
  1785. function TrimTrailPrimPChar(S : PAnsiChar) : PAnsiChar; assembler;
  1786.   {-Return a string with trailing white space removed}
  1787. asm
  1788.   les     di,S
  1789.   mov     dx,es
  1790.   mov     bx,di
  1791.   xor     al,al
  1792.   cld
  1793.   mov     cx,0FFFFh
  1794.   repne   scasb
  1795.   not     cx
  1796.   dec     cx
  1797.   jcxz    @@ExitPoint
  1798.   dec     di
  1799.   dec     di
  1800. @@L0:
  1801.   cmp     byte ptr es:[di],' '
  1802.   ja      @@AllDone
  1803.   dec     di
  1804.   loop    @@L0
  1805. @@AllDone:
  1806.   inc     di
  1807.   mov     byte ptr es:[di],0
  1808. @@ExitPoint:
  1809.   mov     ax,bx
  1810. end;
  1811. {$ENDIF}
  1812.  
  1813. function TrimTrailPChar(Dest, S : PAnsiChar) : PAnsiChar;
  1814.   {-Return a string with trailing white space removed}
  1815. begin
  1816.   StrCopy(Dest, S);
  1817.   Result := TrimTrailPrimPChar(Dest);
  1818. end;
  1819.  
  1820. {$IFDEF Win32}
  1821. function UpCaseChar(C : AnsiChar) : AnsiChar; register;
  1822. asm
  1823.   and   eax, 0FFh
  1824.   push  eax
  1825.   call  CharUpper
  1826. end;
  1827. {$ELSE}
  1828. function UpCaseChar(C : AnsiChar) : AnsiChar; assembler;
  1829. asm
  1830.   mov     al,C
  1831.   call    UpcasePrim
  1832. end;
  1833. {$ENDIF}
  1834.  
  1835. end.
  1836.